package deyi.udp;

import deyi.cache.AppCache;
import deyi.log.AppLogger;
import deyi.nbtype.DeviceDataSign;
import deyi.nbtype.DeviceIpMap;
import deyi.nbtype.DeviceMetaData;
import deyi.utils.CommonUtil;
import deyi.utils.NBHandler;
import deyi.utils.SpringContextUtil;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class UdpEventHandle extends IoHandlerAdapter {
    public UdpEventHandle() {
        super();
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        super.sessionCreated(session);
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        super.sessionOpened(session);
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        super.sessionClosed(session);
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        super.sessionIdle(session, status);
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        AppLogger.logError(cause);
        session.close(true);
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        Date date = new Date();
        if(message instanceof IoBuffer){
            IoBuffer revBuffer = (IoBuffer)message;
            if(revBuffer.hasArray()){
                //解析块协议，获取相应标志
                DeviceDataSign deviceDataSign = NBHandler.getDeviceDataSign(revBuffer.array());
                AppCache appcache = (AppCache) SpringContextUtil.getBean("appcache");
                //把设备的ip port放入列表
                InetSocketAddress remoteAddr = (InetSocketAddress) session.getRemoteAddress();
                String hostName = remoteAddr.getAddress().getHostAddress();
                int port = remoteAddr.getPort();
                AppLogger.logInfo("middle received device data " + " [devicesn]: " + deviceDataSign.getSn() + " [block num]: " + deviceDataSign.getBlockNum() + " [frame num]: " + deviceDataSign.getBlockFrameNum() + deviceDataSign.getSn() + " [device data]: " + CommonUtil.byte2HexStr(revBuffer.array()));
                appcache.getDeviceMap().put(deviceDataSign.getSn(),new DeviceIpMap(hostName,port));

                //获取设备传输的数据
                byte[] nbdata = new byte[deviceDataSign.getBlockFrameLen()];
                System.arraycopy(revBuffer.array(),18,nbdata,0,deviceDataSign.getBlockFrameLen());
                //单帧传输，直接放入缓存
                if(deviceDataSign.getBlockSig() == 2 && deviceDataSign.getBlockFrameNum() == 0){

                    appcache.getDeviceAppDataList().addObj(new DeviceMetaData(deviceDataSign.getSn(),nbdata,date));
                    if(deviceDataSign.getReliableTrans() == 1){
                        byte[] firstResponse = NBHandler.reponseBlockFrame(revBuffer.array()[0],revBuffer.array()[1],revBuffer.array()[2], deviceDataSign.getSnBytes(),false);
                        IoBuffer replyFirstResponse= IoBuffer.wrap(firstResponse);
                        WriteFuture wf = session.write(replyFirstResponse);
                    }
                }
                //首帧传输
                if(deviceDataSign.getBlockSig() == 0 && deviceDataSign.getBlockFrameNum() == 0){
                    String blockSign = deviceDataSign.getSn() + "-" + deviceDataSign.getBlockNum();
                    ConcurrentHashMap<Integer,byte[]> blockFrame = appcache.getDeviceBlockDataMap().get(blockSign);
                    if(blockFrame == null){
                        blockFrame = new ConcurrentHashMap<Integer, byte[]>();
                        blockFrame.put(deviceDataSign.getBlockFrameNum(),nbdata);
                        appcache.getDeviceBlockDataMap().put(blockSign,blockFrame);
                    }else{
                        blockFrame.clear();
                        blockFrame.put(deviceDataSign.getBlockFrameNum(),nbdata);
                    }
                    //给设备发送应答帧
                    byte[] firstResponse = NBHandler.reponseBlockFrame(revBuffer.array()[0],revBuffer.array()[1],revBuffer.array()[2], deviceDataSign.getSnBytes(),true);
                    IoBuffer replyFirstResponse= IoBuffer.wrap(firstResponse);
                    WriteFuture wf = session.write(replyFirstResponse);
                    AppLogger.logInfo("middle received block first frame " + " [devicesn]: " + deviceDataSign.getSn() + " [block num]: " + deviceDataSign.getBlockNum() + " [frame num]: " + deviceDataSign.getBlockFrameNum() + deviceDataSign.getSn());
                }
                //中间帧传输
                if(deviceDataSign.getBlockSig() == 1 && deviceDataSign.getBlockFrameNum() > 0){
                    String blockSign = deviceDataSign.getSn() + "-" + deviceDataSign.getBlockNum();
                    ConcurrentHashMap<Integer,byte[]> blockFrame = appcache.getDeviceBlockDataMap().get(blockSign);
                    blockFrame.put(deviceDataSign.getBlockFrameNum(),nbdata);
                }
                //尾帧传输
                if(deviceDataSign.getBlockSig() == 2 && deviceDataSign.getBlockFrameNum() > 0){
                    String blockSign = deviceDataSign.getSn() + "-" + deviceDataSign.getBlockNum();
                    ConcurrentHashMap<Integer,byte[]> blockFrame = appcache.getDeviceBlockDataMap().get(blockSign);
                    if(blockFrame == null){
                        //没有找到，单独传输尾帧，有可能是错误
                        byte[] endResponse = NBHandler.reponseBlockFrame(revBuffer.array()[0],revBuffer.array()[1],revBuffer.array()[2],deviceDataSign.getSnBytes(),false);
                        IoBuffer replyEndResponse= IoBuffer.wrap(endResponse);
                        WriteFuture wfend = session.write(replyEndResponse);
                        session.close(true);
                        AppLogger.logInfo("middle find tail frame but not find first frame " + " [devicesn]: " + deviceDataSign.getSn() + " [block num]: " + deviceDataSign.getBlockNum() + " [frame num]: " + deviceDataSign.getBlockFrameNum());
                        return;
                    }
                    blockFrame.put(deviceDataSign.getBlockFrameNum(),nbdata);
                    //进行缺帧校验
                    List<Integer> listLackFrame = new ArrayList<Integer>();
                    for(int i=0; i<=deviceDataSign.getBlockFrameNum(); i++){
                        if(!blockFrame.containsKey(i))
                            listLackFrame.add(i);
                    }

                    if(listLackFrame.size() > 0){
                        //有缺帧出现
                        byte[] lackResponse = NBHandler.responseBlockLackFrame(listLackFrame,revBuffer.array()[0],revBuffer.array()[1],revBuffer.array()[2],deviceDataSign.getSnBytes());
                        IoBuffer replyLackResponse= IoBuffer.wrap(lackResponse);
                        WriteFuture wf = session.write(replyLackResponse);
                        AppLogger.logInfo("middle find block lack frame " + " [devicesn]: " + deviceDataSign.getSn() + " [block num]: " + deviceDataSign.getBlockNum() + " [frame num]: " + deviceDataSign.getBlockFrameNum() + " [lack frame]: " + listLackFrame.toString());
                    }
                    else{
                        //给设备发送接收完整应答帧
                        byte[] endResponse = NBHandler.reponseBlockFrame(revBuffer.array()[0],revBuffer.array()[1],revBuffer.array()[2],deviceDataSign.getSnBytes(),false);
                        IoBuffer replyEndResponse= IoBuffer.wrap(endResponse);
                        WriteFuture wf = session.write(replyEndResponse);
                        //把完整帧放入缓存
                        byte[] tmpBytes = blockFrame.get(0);
                        for(int i=1; i<=deviceDataSign.getBlockFrameNum(); i++){
                            byte[] data = blockFrame.get(i);
                            tmpBytes = CommonUtil.mergeByteArray(tmpBytes,data);
                        }
                        appcache.getDeviceBlockDataMap().remove(blockSign);
                        appcache.getDeviceAppDataList().addObj(new DeviceMetaData(deviceDataSign.getSn(),tmpBytes,date));
                        AppLogger.logInfo("middle received whole block" + " [devicesn]: " + deviceDataSign.getSn() + " [block num]: " + deviceDataSign.getBlockNum() + " [whole frame num]: " + deviceDataSign.getBlockFrameNum());

                    }
                }

                //配置设备
                ConcurrentHashMap<Integer,byte[]> config = appcache.getDeviceConfigMap().get(deviceDataSign.getSn());
                if(config != null){
                    //如果有该设备的配置，则首先发送配置给设备
                    for(Map.Entry<Integer,byte[]> entry : config.entrySet()){
                        Integer keyConfig = entry.getKey();
                        byte[] configData = entry.getValue();
                        byte[] nbConfig = NBHandler.getConfigFrame(deviceDataSign.getSn(), configData);
                        IoBuffer replyBuf = IoBuffer.wrap(nbConfig);
                        WriteFuture wf = session.write(replyBuf);
                        AppLogger.logInfo("middle send normal config to device " + " [devicesn]: " + deviceDataSign.getSn() + " [config num]: " + keyConfig + " [config data]: " + CommonUtil.byte2HexStr(configData));
                    }
                    appcache.getDeviceConfigMap().remove(deviceDataSign.getSn());
                }
                session.close(true);
            }
        }
    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        super.messageSent(session, message);
    }
}
